library input;

uses CoreI, common, sdl, InputI, sysutils, lists;
type PInputAct=^TInputAct;
TInputAct=record
action:integer;
data1,data2:pointer;
Combination:TCombination;
CombinationNum:byte;
WasPressed:boolean;
end;
var InputActions:array[1..MaxKeysInCombination] of PList;
{$i keys.inc}
function StrToCombination(const key:PChar; var comb:TCombination):byte;
var k:string;
i:cardinal;
p:integer;
begin
             k:= key;
             for i := 0 to MaxKeysInCombination -1 do
             comb[i] := 0;
             for i := 0 to MaxKeysInCombination -1 do begin
if k = '' then break;
p:=pos('+', k);
if p < 2 then begin
comb[i] := str2key(k);
                             if comb[i] = 0 then
Log_WriteF('input.StrToCombination: error converting specified value (unknown key ''%s'')', ll_warn, [PChar(k)]);
k:='';
end else begin //p > 1
comb[i] := str2key(copy(k,1,p -1));
if comb[i] = 0 then
Log_WriteF('input.StrToCombination: error converting specified value (unknown key ''%s'')', ll_warn, [PChar(k)]);
delete(k, 1, p);
end;
end;
result := i -1;
             end;
                                      function CombinationsEqual(c1, c2:TCombination):boolean;
var i:integer;
begin
result:=true;
for i:=0 to MaxKeysInCombination -1 do
result := result and (c1[i] = c2[i]);
                                      end;
Function IsRegistered(const sym:PChar):boolean; cdecl;
             var PWalk:PList;
             comb:TCombination;
i:integer;
             begin
             result:=false;
             StrtoCombination(sym, comb);
             for i:= 1 to MaxKeysInCombination do begin
             PWalk:=InputActions[i];
while PWalk <> nil do begin
if CombinationsEqual(PInputAct(PWalk^.Data)^.Combination, comb) then begin
result:=true;
break;
end;
PWalk:=PWalk^.Next;
end;
end;
end;

             Function CombinationLength(const comb:TCombination):byte;
var i:integer;
             begin
result:=0;
              for i:=0 to MaxKeysInCombination -1 do
              if comb[i] >0 then inc(result);
             end;

             Procedure RegisterInputAction(const combination:TCombination; action:integer; data1,data2:pointer); cdecl;
var act:PInputAct;
begin
new(act);
act^.action := action;
act^.combination := combination;
act^.data1 := data1;
act^.data2 := data2;
act^.CombinationNum := CombinationLength(combination);
act^.WasPressed := false;
List_InsertBefore(InputActions[act^.combinationNum], InputActions[act^.combinationNum], act);
end;

Procedure RegisterInputActionStr(const str:PChar; action:integer; data1,data2:pointer); cdecl;
var comb:TCombination;
begin
                      StrToCombination(str, comb);
                      RegisterInputAction(comb, action, data1, data2);
end;

                                                                            Function InputEventHandler(event:TSDL_Event):boolean; cdecl;
var PWalk:PList;
act:PInputAct;
type keystates=array[0..400] of boolean;
var keys:^KeyStates;
i:integer;
procedure PushEvent(code:integer);
var event:TSDL_Event;
begin
event.type_ := EVENT_INPUTACTION;
event.user.code := code;
new(PInputAction(event.user.data1));
                             PInputAction(event.user.data1)^.action := act^.action;
                             PInputAction(event.user.data1)^.data1 := act^.data1;
                             PInputAction(event.user.data1)^.data2 := act^.data2;
sdl_pushevent(@event);
end;
var j:integer;
                                                                            begin
result:=false;
keys:=nil;
for j:=MaxKeysInCombination downto 1 do begin
PWalk:=InputActions[j];
while PWalk <> nil do begin
act:=PWalk^.Data;
if event.key.keysym.sym = act^.Combination[act^.CombinationNum -1] then //last key in combination?
case event.key.state of
SDL_PRESSED: begin
act^.WasPressed := true;
          if keys=nil then keys:=pointer(SDL_GetKeyState(nil));
          for i:=0 to act^.CombinationNum -2 do //checking all keys in combination excluding last
          act^.WasPressed := act^.WasPressed and keys[act^.Combination[i]];
if act^.WasPressed then begin
PushEvent(INPUTACTION_DOWN);
exit;
end;
                                                end;
          SDL_RELEASED: if act^.WasPressed then begin
          PushEvent(INPUTACTION_UP);
          act^.WasPressed := false;
exit;
          end;
end;
          PWalk:=PWalk^.Next;
end;
end;
                                                                            end;
               Procedure UnregisterInputAction(const str:PChar); cdecl; overload;
                                                                            var PWalk:PList;
comb:TCombination;
 begin
strToCombination(str, comb);
PWalk:=InputActions[CombinationLength(comb)];
                                                                                            while PWalk <> nil do begin
if CombinationsEqual(PInputAct(PWalk^.Data)^.Combination, comb) then begin
list_removenode(InputActions[CombinationLength(comb)], PWalk);
break;
end;
                                                                                            PWalk := PWalk^.Next;
                                                                                            end;
 end;
 Procedure UnregisterInputAction(const action:integer; data1:pointer); cdecl; overload;
                                                                            var PWalk:PList;
i:integer;
                                                                            begin
for i:= 1 to MaxKeysInCombination do begin
                                                                                            PWalk:=InputActions[i];
                                                                                            while PWalk <> nil do begin
if (PInputAct(PWalk^.Data)^.action = action) and (PInputAct(PWalk^.Data)^.data1 = data1) then begin
list_removenode(InputActions[i], PWalk);
exit;
end;
                                                                                            PWalk := PWalk^.Next;
                                                                                            end;
                                                                                            end;
                                                                                            end;

var PluginInfo:PPluginInfo=nil;

procedure Init; cdecl;
                            begin
Core_RegisterEventHandler(sdl_keydown, InputEventHandler);
Core_RegisterEventHandler(sdl_keyup, InputEventHandler);
                            end;

                            Procedure Free; cdecl;
var i:integer;
                            begin
dispose(IInput(PluginInfo^.InterfaceStruct));
dispose(PluginInfo);
Core_UnregisterEventHandler(InputEventHandler);
Core_UnregisterEventHandler(InputEventHandler);
              for i:=1 to MaxKeysInCombination do
              list_freeall(InputActions[i]);
                            end;

Procedure Plugin_GetInfo(var info:PPluginInfo; var length:cardinal); cdecl;
begin
new(info);
PluginInfo:=info;
info^.Name := 'Input';
info^.GUID := InputSystemGUID;
info^.Init := init;
info^.free := free;
new(IInput(Info^.InterfaceStruct));
IInput(Info^.InterfaceStruct)^.RegisterInputAction := RegisterInputAction;
IInput(Info^.InterfaceStruct)^.UnregisterInputAction := UnregisterInputAction;
IInput(Info^.InterfaceStruct)^.RegisterInputActionStr := RegisterInputActionStr;
IInput(info^.InterfaceStruct)^.UnregisterInputActionStr := UnregisterInputAction; 
length:=1;
IInput(Info^.InterfaceStruct)^.IsRegistered := IsRegistered; 
                             end;

                            exports Plugin_GetInfo;
begin
end.

